﻿using System;
using Base.Core;
using System.IO;
using WpfAi.Model;
using System.Drawing;
using System.Threading;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace WpfAi.Core
{
    /// <summary>
    /// 报文解析
    /// </summary>
    public class Analysis
    {
        #region __类内变量___
        public static object CCCDataQueLock = new object();
        public static Queue<byte[]> CCCDataQue = new Queue<byte[]>();
        Thread threadReadCCCDataQue;
        ///////////以下为图片用到的内存///////////
        static int bmpMapBuffer = 640;
        static int bmpMapWidth = bmpMapBuffer / 2;
        static int bmpMapHeight = 240;
        static byte[] fullPicBuffer = new byte[bmpMapBuffer * bmpMapHeight];
        #endregion

        #region __构造函数___
        public Analysis()
        {
            Base.Core.ThreadHelper.StartBgThread(ref threadReadCCCDataQue, new ParameterizedThreadStart(ReadCCCAll), null, true);
        }
        #endregion

        #region ___协议报文各个缓冲区___
        private static readonly int BufRecvSize = 1024 * 400;
        /// <summary>
        /// BufRecvSckData的实际内容长度
        /// </summary>
        private int BuffRecvLen = 0;

        private byte[] BufRecv = new byte[BufRecvSize];

        private const byte MSG_SPLIT_FLAG = 0xA5;
        #endregion

        int i = 0;
        /// <summary>
        /// 入栈报文
        /// </summary>
        /// <param name="data"></param>
        public void Enqueue(byte[] data)
        {
            lock (CCCDataQueLock)
            {
                CCCDataQue.Enqueue(data);
                //LogHelper.Log.Info("入列：" + SerialClass.ByteToString(data));
            }
        }
        /// <summary>
        /// 解析报文
        /// </summary>
        /// <param name="state"></param>
        public void ReadCCCAll(object stall)
        {
            try
            {
                while (true)
                {
                    while (CCCDataQue.Count > 0)
                    {
                        byte[] msg = null;
                        lock (CCCDataQueLock)
                        {
                            if (CCCDataQue.Count > 0)
                            {
                                //msg = new byte[] {
                                //    0xA5, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2E, 0x0A, 0x03, 0x0C, 0x00, 0x30, 0x60,
                                //    0xA5, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2E, 0x0A, 0x03, 0x0C, 0x00, 0x30, 0x60,
                                //    0x01,0x02
                                //};
                                msg = CCCDataQue.Dequeue();
                            }
                        }
                        if (msg != null && msg.Length > 0)//msg是SOCKET收到原始消息，可能是消息的某一部分，也可能包含几人完整消息
                        {
                            if (msg.Length > BufRecvSize)//收到一个超大消息包
                            {
                                continue;
                            }
                            if (msg.Length + BuffRecvLen > BufRecvSize)//收到一个超大消息包
                            {
                                BuffRecvLen = 0;
                            }
                            //收到合适的数据包，可以放入拆包缓存
                            Array.Copy(msg, 0, BufRecv, BuffRecvLen, msg.Length);
                            BuffRecvLen += msg.Length;//实际内容长度增加
                            int one7eEndIdx = 0;
                            for (int i = 0; i < BuffRecvLen; i++)//循环的所有数据内容
                            {
                                if (BufRecv[i] == MSG_SPLIT_FLAG
                                    && BufRecv[i + 1] == MSG_SPLIT_FLAG
                                    && BufRecv[i + 2] == 0x00
                                    && BufRecv[i + 3] == 0x00
                                    && BufRecv[i + 4] == 0x00
                                    && BufRecv[i + 5] == 0x00
                                    && BufRecv.Length >= i + 7)//找到正确数据报文的起始0xA5
                                {
                                    ////////数据长度///
                                    var length = (0xff00 & BufRecv[i + 6 + 1] << 8 | 0xff & BufRecv[i + 6]);
                                    ////头(12)+命令(2)+数据长度N(N)+CRC(2)
                                    byte[] one7e = new byte[12 + length + 2];
                                    if ((BuffRecvLen - one7eEndIdx) < one7e.Length)
                                    {
                                        Array.Copy(BufRecv, i, one7e, 0, BuffRecvLen);
                                        LogHelper.Log.Info("数据长度不足【" + i + "】要求长度:" + one7e.Length + ",当前长度:" + BuffRecvLen + ",解析数据:" + SerialClass.ByteToString(one7e));
                                        break;
                                    }
                                    Array.Copy(BufRecv, i, one7e, 0, one7e.Length);
                                    UnPackage(one7e);
                                    one7eEndIdx += one7e.Length;
                                    i = one7eEndIdx - 1;
                                }
                            }
                            if (one7eEndIdx > 0)
                            {
                                //如果包内容索引已经大于等于缓冲区的实际数据大小，则说明缓冲区的包刚好拆完，没有不完整包。则直接将缓冲区实际大小置0
                                if (one7eEndIdx >= BuffRecvLen)
                                    BuffRecvLen = 0;
                                //如果包内容索引小于缓冲区实际内容长度，则将包内容索引之前数据清理掉，方法是将其后的数据向前移至缓冲区的开头
                                else
                                {
                                    BuffRecvLen = BuffRecvLen - one7eEndIdx;
                                    if (0 == BuffRecvLen)
                                    {
                                        BufRecv = new byte[BufRecvSize];
                                        msg = new byte[msg.Length];
                                    }
                                    else
                                        Array.Copy(BufRecv, one7eEndIdx, BufRecv, 0, BufRecv.Length - one7eEndIdx);
                                }
                            }
                        }
                    }
                    Thread.Sleep(100);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Log.Error("解包发生错误:" + ex.Message);
            }
        }
        /// <summary>
        /// 回应报文
        /// </summary>
        /// <param name="Message"></param>
        /// <returns></returns>
        public ResponseWarp UnPackage(byte[] Message)
        {
            ResponseWarp r = new ResponseWarp();
            ////////数据长度///
            var dataLength = new byte[2];
            Array.Copy(Message, 6, dataLength, 0, 2);
            var length = (0xff00 & dataLength[1] << 8 | 0xff & dataLength[0]) - 2;
            if (Message.Length < length)
                return null;
            ////////数据////
            var data = 0;
            if (length > 0)
            {
                var byteData = new byte[length];
                Array.Copy(Message, 14, byteData, 0, length);
                r.DataBuffer = byteData;
                data = 0xff & byteData[0];
            }
            ////////命令///////
            var commandID = new byte[2];
            Array.Copy(Message, 12, commandID, 0, 2);
            Array.Reverse(commandID);
            var cmd = commandID[0] << 8 | commandID[1];
            r.Command = cmd;
            r.HexByte = SerialClass.byteToHexStr(Message);
            r.Message = "测试通过";
            if (cmd == 0x0c02)
            {
                r.Success = true;
                r.Remark = "【复位】PC->设备";
            }
            if (cmd == 0x0c03)
            {
                r.Success = data == 0;
                if (!r.Success)
                    r.Message = "测试失败";
                r.Remark = "【复位】设备->PC";
                r.Title = "复位";
            }
            if (cmd == 0x0c04)
            {
                r.Success = true;
                r.Remark = "【状态查询】PC->设备";
            }
            if (cmd == 0x0c05)
            {
                r.Success = data == 0;
                if (!r.Success)
                    r.Message = data == 1 ? "监视器板未连接" : "摄像头未连接";
                r.Remark = "【状态查询】设备->PC";
                r.Title = "状态查询";
            }
            if (cmd == 0x0c06)
            {
                r.Success = true;
                r.Remark = "【三色灯】PC->设备";
            }
            if (cmd == 0x0c07)
            {
                r.Success = data == 0;
                r.Remark = "【三色灯】设备->PC";
                if (!r.Success)
                    r.Message = "指令响应异常";
                r.Title = "三色灯";
            }
            if (cmd == 0x0c08)
            {
                r.Success = true;
                r.Remark = "【14Pin公座】PC->设备";
                r.Title = "14Pin公座";
            }
            if (cmd == 0x0c09)
            {
                r.Success = data == 0;
                r.Remark = "【14Pin公座】设备->PC";
                if (!r.Success)
                    r.Message = "指令响应异常";
                r.Title = "14Pin公座";
            }
            if (cmd == 0x0c0A)
            {
                r.Success = true;
                r.Remark = "【核心板按键】PC->设备";
                r.Title = "核心板按键";
            }
            if (cmd == 0x0c0B)
            {
                r.Success = data == 0;
                r.Remark = "【核心板按键】设备->PC";
                if (!r.Success)
                    r.Message = "按键超时未按下";
                r.Title = "核心板按键";
            }
            if (cmd == 0x0c0C)
            {
                r.Success = true;
                r.Remark = "【屏幕显示】PC->设备";
                r.Title = "屏幕显示";
            }
            if (cmd == 0x0c0D)
            {
                r.Success = data == 0;
                r.Remark = "【屏幕显示】设备->PC";
                if (!r.Success)
                    r.Message = "指令响应异常";
                r.Title = "屏幕显示";
            }
            if (cmd == 0x0c0E)
            {
                r.Success = true;
                r.Remark = "【触摸屏】PC->设备";
                r.Title = "触摸屏";
            }
            if (cmd == 0x0c0F)
            {
                r.Success = data == 0;
                r.Remark = "【触摸屏】设备->PC";
                if (!r.Success)
                    r.Message = "触摸超时未响应";
                r.Title = "触摸屏";
            }
            if (cmd == 0x0c10)
            {
                r.Success = true;
                r.Remark = "【监视器双按键】PC->设备";
                r.Title = "监视器双按键";
            }
            if (cmd == 0x0c11)
            {
                r.Success = data == 0;
                r.Remark = "【监视器双按键】设备->PC";
                if (!r.Success)
                    r.Message = "指令响应异常";
                r.Title = "监视器双按键";
            }
            if (cmd == 0x0c12)
            {
                r.Success = true;
                r.Remark = "【蜂鸣器】PC->设备";
                r.Title = "蜂鸣器";
            }
            if (cmd == 0x0c13)
            {
                r.Success = data == 0;
                r.Remark = "【蜂鸣器】设备->PC";
                if (!r.Success)
                    r.Message = "按键超时未响应";
                r.Title = "蜂鸣器";
            }
            if (cmd == 0x0c14)
            {
                r.Success = true;
                r.Remark = "【监视器TF卡】PC->设备";
                r.Title = "监视器TF卡";
            }
            if (cmd == 0x0c15)
            {
                r.Success = data == 0;
                r.Remark = "【监视器TF卡】设备->PC";
                if (!r.Success)
                    r.Message = data == 1 ? "TF卡未插入" : "TF卡读写错误";
                r.Title = "监视器TF卡";
            }
            if (cmd == 0x0c16)
            {
                r.Success = true;
                r.Remark = "【摄像头】PC->设备";
                r.Title = "摄像头";
            }
            if (cmd == 0x0c17)
            {
                r.Success = data == 0;
                r.Remark = "【摄像头】设备->PC";
                if (!r.Success)
                    r.Message = "指令响应异常";
                r.Title = "摄像头";
            }
            if (cmd == 0x0c18)
            {
                r.Success = data == 0;
                r.Remark = "【心跳】PC->设备";
                r.Title = "心跳";
            }
            if (cmd == 0x0c19)
            {
                r.Success = data == 0;
                r.Remark = "【心跳】设备->PC";
                r.Title = "心跳";
            }
            if (cmd == 0x0c1a)
            {
                r.Success = true;
                r.Remark = "【14Pin母座】PC->设备";
                r.Title = "14Pin母座";
            }
            if (cmd == 0x0c1b)
            {
                r.Success = data == 0;
                r.Remark = "【14Pin母座】设备->PC";
                if (!r.Success)
                    r.Message = "有错误:";
                r.Title = "14Pin母座";
            }
            if (cmd == 0x0c1D)
            {
                var number = new byte[1];
                Array.Copy(Message, 14, number, 0, number.Length);
                r.Number = (int)number[0];
                Array.Copy(Message, 15, r.Buffer, 0, r.Buffer.Length);
                r.Success = r.Number == 239;
                i = i + Message.Length;
                //LogHelper.Log.Debug("收到数据包:【" + r.Number + "】,数据总长度【" + i + "】,当前包长度【" + Message.Length + "】");
                //if (Message.Length > 1024)
                //LogHelper.Log.Debug("超长数据包:" + SerialClass.ByteToString(Message));
                Array.Copy(r.Buffer, 0, fullPicBuffer, r.Buffer.Length * r.Number, r.Buffer.Length);
                ///////图片数据接收已满///
                if (r.Number == 239)
                {
                    i = 0;
                    r.Success = true;
                    r.CameraPicPath = GetBmpPicture(bmpMapWidth, bmpMapHeight, fullPicBuffer);
                }
                r.Remark = "【图片上传】设备->PC";
                if (!r.Success)
                    r.Message = "包不完整";
                r.Title = "图片上传";
            }
            if (cmd == 0x0c21)
            {
                r.Success = data == 0;
                r.Remark = "【写核心单板SN】设备->PC";
                if (!r.Success)
                    r.Message = "写入错误";
                r.Title = "写核心单板SN";
            }
            if (cmd == 0x0c23)
            {
                r.Success = data == 0;
                r.Remark = "【读核心单板SN】设备->PC";
                if (!r.Success)
                    r.Message = "读取错误";
                r.Title = "读核心单板SN";
            }
            this.TriggerEvent(this, r);
            //r.Message += "【报文】" + SerialClass.ByteToString(Message);
            return r;
        }
        /// <summary>
        /// 上报事件
        /// </summary>
        public event EventHandler<ResponseWarp> OnPlate;
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public void TriggerEvent(object sender, ResponseWarp args)
        {
            if (null != OnPlate)
            {
                OnPlate(sender, args);
            }
        }
        /// <summary>
        /// 保存图片
        /// </summary>
        /// <param name="w"></param>
        /// <param name="h"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public string GetBmpPicture(int w, int h, byte[] data)
        {
            var fileName = DateTime.Now.ToString("yyyyMMddHHmmss") + ".bmp";
            try
            {
                Bitmap bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);
                Rectangle rect = new Rectangle(0, 0, w, h);
                BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format16bppRgb565);
                IntPtr ptrToFirstPixel = bmpData.Scan0;
                Marshal.Copy(fullPicBuffer, 0, ptrToFirstPixel, fullPicBuffer.Length);
                bmp.UnlockBits(bmpData);
                var fullPath = Path.Combine(GlobalEnvironment.PhotoStorageUrl, fileName);
                bmp.Save(fullPath, System.Drawing.Imaging.ImageFormat.Bmp);
                LogHelper.Log.Info("保存图片:" + fullPath);
            }
            catch (Exception ex)
            {
                LogHelper.Log.Info("保存图片出错:" + ex.Message);
            }
            return fileName;
        }
    }
}